using System;
using System.Text;
using System.Collections;
using System.Collections.ObjectModel;
using System.Runtime.Serialization;
using System.Reflection;

using Crainiate.Data.Persistence.Cache;

namespace Crainiate.Data.Persistence
{
	[Serializable]
    public class Conditions: Collection<ICondition>, IConditions, ISerializable, IDeserializationCallback
	{
		//Property variables
		private Operator _operator;
        private IConditions _subConditions;

        //Working variables
        private SerializationInfo mInfo;
        private int _cache;
        private int _subcache;

		#region Interface

		//Constructors
		public Conditions()
		{
            _cache = 0;
            _subcache = 0;
		}

		public Conditions(Operator value)
		{
			_operator = value;
            _cache = 0;
            _subcache = 0;
		}

        protected Conditions(SerializationInfo info, StreamingContext context)
        {
            mInfo = info;    
        }

		//Properties
		public virtual Operator Operator
		{
			get
			{
				return _operator;
			}
			set
			{
				_operator = value;
                _cache = 0;
			}
		}

        public virtual IConditions SubConditions
        {
            get
            {
                return _subConditions;
            }
            set
            {
                _subConditions = value;
                _subcache = 0;
            }
        }

        //Methods
        public ICondition[] ToArray()
        {
            ICondition[] destinationArray = new ICondition[Count];
            this.CopyTo(destinationArray, 0);
            return destinationArray;
        }

        public virtual void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
        {
            info.AddValue("Conditions", ToArray());
            info.AddValue("Operator", Convert.ToInt32(Operator).ToString());
            info.AddValue("SubConditions", SubConditions);
        }

        public virtual void OnDeserialization(object sender)
        {
            ICondition[] array = (ICondition[]) mInfo.GetValue("Conditions", typeof(ICondition[]));

            Clear();
            foreach (ICondition condition in array)
            {
                Add(condition);
            }

            _operator = (Operator) Enum.Parse(typeof(Operator), mInfo.GetString("Operator"));
            _subConditions = (Conditions) mInfo.GetValue("SubConditions", typeof(Conditions));

            mInfo = null;
            _cache = 0;
            _subcache = 0;
        }

        //Overrides
        protected override void ClearItems()
        {
            _cache = 0;
            base.ClearItems();
        }

        protected override void InsertItem(int index, ICondition item)
        {
            _cache = 0;
            base.InsertItem(index, item);
        }

        protected override void RemoveItem(int index)
        {
            _cache = 0;
            base.RemoveItem(index);
        }

        public sealed override int GetHashCode()
        {
            //Return from cache if the collection is unchanged and the subcollection is unchanged
            int subcache = 0;
            if (_subConditions != null) subcache = _subConditions.GetHashCode();
            if (_cache != 0 && _subcache == subcache) return _cache;

            //Create using a string builder
            StringBuilder builder = new StringBuilder();

            //Add any subquery hash
            if (_subConditions != null) builder.Append(subcache);

            //Add each condition
            foreach (ICondition condition in this)
            {
                PropertyInfo prop = condition.PropertyInfo;
            
                if (typeof(IView).IsAssignableFrom(prop.ReflectedType))
                {
                    ReflectionCacheItem cache = Component.Instance.ReflectionCache.Lookup(prop.ReflectedType);
                    prop = cache.GetOriginalProperty(prop);
                }

                builder.Append(prop.ToString());
                builder.Append(condition.Comparison.ToString());
            }

            //Cache for next time
            _cache = builder.ToString().GetHashCode();
            _subcache = subcache;

            return _cache;
        }

        #endregion
	}
}

